<script>
import {
  toRaw,
  h,
  ref,
  reactive,
  resolveComponent,
  isProxy,
  vShow,
  resolveDirective,
  withDirectives,
  getCurrentInstance,
  onBeforeUnmount,
  watch,
  defineComponent,
  withKeys,
  withModifiers
} from 'vue'
import {useInjectGridContext} from "@/components/grid/context";
import {useProvideRenderContext} from "./context";
import {setObjectValue, getObjectValue, isNumber, setProxyData} from "@/utils/index"
import splitCode from "@/utils/splitCode"
import ajax from '@/utils/ajax'
import {forEach,cloneDeep} from 'lodash'
import {state,action} from "@/store";
import bus from "@/store/bus.js";
import {message, notification,Select} from "ant-design-vue";
import {responseBefore} from "@/utils/axios";
export default defineComponent({
  name: "render",
  props: {
    data: {
      type: [String,Object],
      default: '',
    },
    scopeProp:{
      type: Object,
      default: {},
    },
    context: {
      type: Object,
      default: {},
    }
  },
  render() {
    if (this.data) {
      const data = toRaw(this.data)
      this.setProxyData(data, 1,this.scopeProp.row ?this.scopeProp.row:this.modelValue)
      let whereAnd = [],whereOr = []
      if(data.where){
        whereAnd = data.where.AND || []
        whereOr = data.where.OR || []
      }

      return this.where(whereAnd, whereOr) ? this.renderComponent(data,this.scopeProp,this.$slots) : null
    }
    return null
  },
  setup(props,ctx) {
    const instance = getCurrentInstance()
    let grid = useInjectGridContext()
    let form
    const modelValue = reactive(props.context)
    state.modelValue = modelValue
    const componentKey = []
    useProvideRenderContext(modelValue)
    watch(()=>props.data,(value,oldValue)=>{
      if (oldValue) {
        setProxyData(oldValue, 0,modelValue)
      }
    })
    onBeforeUnmount(() => {
      componentKey.forEach(key => {
        delete instance.appContext.components[key]
      })
      if (props.data) {
        setProxyData(props.data, 0,modelValue)
      }
    })
    function renderComponent(data, scopeProp,slots = {}) {
      if (!data.attribute) {
        return
      }
      let children = {}, name, attribute = {...data.attribute}
      let updateObject = null
      if (scopeProp && scopeProp.row) {
        //formMany遍历
        updateObject = scopeProp.row
        attribute.hasManyRow = updateObject
      } else {
        updateObject = modelValue

      }
      setProxyData(data,1,updateObject)
      if(data.name == 'DraggableRender' || data.name == 'ExAsyncRender' || data.name == 'ExModal'){
        attribute.scopeProp = scopeProp
      }
      //属性绑定
      for (let bindAttr in data.bindAttribute) {
        if (bindAttr === 'ref') {
          const bindRef = ref()
          if(data.name == 'ExForm'){
            form = bindRef
          }
          modelValue[data.bindAttribute[bindAttr]] = bindRef
          attribute[bindAttr] = bindRef
        } else {

          if(typeof(data.bindAttribute[bindAttr]) == 'string'){
            attribute[bindAttr] = getObjectValue(updateObject, data.bindAttribute[bindAttr])
          }else if(typeof(data.bindAttribute[bindAttr]) == 'object'){

            let bindAttrObject = {}
            forEach(data.bindAttribute[bindAttr],function (value,key){
              bindAttrObject[key] = getObjectValue(updateObject, value)
            })

            if(attribute[bindAttr]){
              Object.assign(attribute[bindAttr],bindAttrObject)
            }else{
              attribute[bindAttr] = bindAttrObject
            }

          }
        }
      }
      //属性绑定函数
      for (let bindFunction in data.bindFunction) {

        attribute[bindFunction] = new Function(...data.bindFunction[bindFunction])

      }
      //绑定bindExpose
      if(data.bindExpose){
        data.bindExpose.forEach(item => {
          if (getObjectValue(modelValue, item.ref)) {
            attribute[item.attr] = getObjectValue(modelValue, item.ref)[item.expose]
          }
        })
      }
      //双向绑定值
      for (let modelBind in data.modelBind) {
        let field = data.modelBind[modelBind]
        attribute[modelBind] = getObjectValue(updateObject, field)
        //select change load
        if(modelBind === 'value' && attribute.changeLoadOptions){
          attribute.changeLoadOptions = attribute.changeLoadOptions.map(item=>{
            return {
              params:item,
              updateObject:updateObject,
            }
          })
        }
        attribute['onUpdate:' + modelBind] = value => {
         if(data.name == 'ExCascader' && modelBind === 'cascaderValue'){
            if(data.attribute.multiple && data.attribute.relation){
              let relationData = []
              value.forEach(row=>{
                let rowValue = {}
                data.attribute.bindField.forEach((bindField,index)=>{
                  rowValue[bindField] = row[index] || 0
                })
                relationData.push(rowValue)
              })
              setObjectValue(updateObject, data.attribute.relation, relationData)
            }else if(data.attribute.bindField){
              if(value){
                data.attribute.bindField.forEach((bindField,index)=>{
                  setObjectValue(updateObject, bindField, value[index] || 0)
                })
              }else{
                data.attribute.bindField.forEach((bindField)=>{
                  setObjectValue(updateObject, bindField, null)
                })
              }
            }
          }
          setObjectValue(updateObject, field, value)
        }
      }


      //事件绑定
      for (let event in data.event) {
        //处理事件修饰符
        let modifiers = []
        let eventName = event
        if(event.indexOf('.') > -1){
          modifiers = event.split('.')
          eventName = modifiers.shift()
        }

        //输入框特殊处理
        if(!data.event.Focus && eventName === 'Blur' && (data.name ==='AInput' || data.name ==='AInputNumber' || data.name === 'AAutoComplete')){
          attribute['onFocus'] = (e) => {
            data.attribute.exFocusValue = e.target.value
          }
          data.event['Keyup.enter'] = JSON.parse(JSON.stringify(data.event[event]))
        }
        //绑定事件
        let eventFunction = (e)=>{
          if(data.attribute.hasOwnProperty('exFocusValue') && data.attribute.exFocusValue == e.target.value){
            return
          }
          ctx.emit(eventName)
          bus.emit(eventName)
          if(typeof(data.event[event]) === 'function'){
            return data.event[event].call(null,e)
          }
          //设置变量
          if (data.event[event].variable) {
            data.event[event].variable.forEach((eventBind) => {
              for (let field in eventBind) {
                setObjectValue(updateObject, field, eventBind[field])
              }
            })
          }
          //执行组件函数
          if (data.event[event].function) {
            data.event[event].function.forEach(item => {
              let refComponent = getObjectValue(modelValue, item.ref)

              if(refComponent){

                if(item.conditionFunction && !new Function(...item.conditionFunction)(e)){
                  return
                }
                getObjectValue(refComponent, item.function).apply(refComponent, item.params)
              }
            })
          }
          //自定义
          if (data.event[event].custom) {
            data.event[event].custom.forEach((item) => {
              let params = item.params
              if (item.type === 'Modal') {
                if(data.modal){
                  data.modal.init = true
                  data.modal.attribute.field = params.modal
                }

                setObjectValue(updateObject, params.modal, true)
              } else if(item.type === 'ModalRefresh'){
                setTimeout(()=>{
                  action.modalRefresh(params)
                })
              }else if (item.type === 'CloseModal' && params.length > 0) {
                setObjectValue(updateObject, params, false)
              } else if (item.type === 'GridRefresh' && grid) {
                grid.exposed.refresh.apply(grid.exposed,item.params)
              }else if (item.type === 'ChangeLoadOptions') {
                changeLoadOptions(params,e,updateObject)
              }else if(item.type === 'Message'){
                message.open(responseBefore(params))
              }else if(item.type === 'Notification'){
                notification.open(responseBefore(params))
              }else if(item.type === 'Ajax'){
                let selected
                if(params.gridBatch){
                  selected = grid.exposed.selectIds.value
                }
                ajax(params.ajax,params.gridBatch,selected,null,updateObject).then(res=>{
                  if(params.gridRefresh){
                    grid.exposed.refresh()
                  }
                })
              }else if(item.type === 'Function'){
                new Function(...params)(...arguments)
              }else if (item.type === 'GridEditable') {
                params.ajax.data.data = {}
                let editValue = modelValue[params.bindField]
                if(editValue === undefined){
                  editValue = null
                }
                params.ajax.data.data[params.ex_admin_field] = editValue
                ajax(params.ajax).then((res)=>{
                  if(params.ex_admin_success){
                    grid.exposed.refresh()
                  }
                })
              }
            })
          }
        }
        if(eventName == 'Keyup'){
          attribute['on' + eventName] = withKeys(eventFunction,modifiers)
        }else if(modifiers.length > 0){
          attribute['on' + eventName] = withModifiers(eventFunction,modifiers)
        }else{
          attribute['on' + eventName] = eventFunction
        }
      }
      //插槽名称对应内容
      for (let slot in data.content) {
        if (data.name == 'ExFormMany') {
          attribute.content = data.content
        }
        children[slot] = (scope) => {
          if (scope === undefined || !isProxy(scope) && (scope instanceof Object && Object.keys(scope).length == 0)) {
            scope = scopeProp
          } else {
            scope = Object.assign(JSON.parse(JSON.stringify(scopeProp)), scope)
          }

          return userRender(data.content[slot], scope)
        }
      }
      if (data.name === 'html') {
        if (data.attribute['data-tag'] === 'component') {
          if (resolveComponent(attribute.key) === attribute.key) {
            getCurrentInstance().appContext.app.component(attribute.key, splitCode(attribute.key,data.content.default[0]))
            componentKey.push(attribute.key)
          }
          delete attribute['data-tag']
          return h(resolveComponent(attribute.key), attribute)
        }else if(data.attribute['html-raw']){
          return h({
            setup() {
              return {
                ...modelValue
              }
            },
            template: data.content.default.join(''),
          })
        } else {
          if(Object.keys(slots).length > 0){
            Object.assign(children,slots)
          }
          return _createVnode(data, data.attribute['data-tag'] || 'span', attribute, children, data.directive)
        }
      }
      if(data.name == 'ASelectOptGroup'){
        name = Select.OptGroup
      }else if(data.name == 'ASelectOption'){
        name = Select.Option
      }else{
        name = resolveComponent(data.name)
      }
      if (scopeProp && scopeProp.row) {
        let validateNameArr = scopeProp.field.split('.')
        validateNameArr.push(scopeProp.$index)
        //遍历AFormItem name
        if (data.name == 'AFormItem' && !data.attribute.noChangeName) {
          attribute.name = scopeProp.recursion.concat(validateNameArr).concat(attribute.name)

          //ExFormMany
        } else if (data.name == 'ExFormMany') {
          attribute.recursion = scopeProp.recursion.concat(validateNameArr)
        }
      }
      if (data.name == 'AFormItem' && attribute.validateFormField) {
        //AFormItem设置校验提示字段绑定
        let validateName = attribute.name.join('.')
        if(!modelValue[attribute.validateFormField]){
          modelValue[attribute.validateFormField] = {}
        }
        if(!modelValue[attribute.validateFormField][validateName]){
          modelValue[attribute.validateFormField][validateName] = {
            status:null,
            message:null,
          }
        }
        attribute.validateStatus = modelValue[attribute.validateFormField][validateName].status
        attribute.help = modelValue[attribute.validateFormField][validateName].message

      }
      //遍历map
      if (data.map && data.map.bindName) {
        let field = data.map.bindName
        let mapData = getObjectValue(updateObject, field) || []
        if (!Array.isArray(mapData)) {
          mapData = []
        }
        return mapData.map(item => {
          for (let attr in data.map.attribute) {
            attribute[attr] = item[data.map.attribute[attr]]
          }
          let mapAttribute = {...attribute}
          if (mapAttribute.slotDefault) {

            if (mapAttribute.slotDefault instanceof Object) {
              children.default = () => renderComponent(toRaw(mapAttribute.slotDefault), {})
            } else {
              children.default = () => mapAttribute.slotDefault
            }
          }
          let mapChildren = {...children}
          return _createVnode(data, name, mapAttribute, mapChildren, data.directive,scopeProp)
        })
      } else {
        if(Object.keys(slots).length > 0){
          Object.assign(children,slots)
        }
        return _createVnode(data, name, attribute, children, data.directive,scopeProp)
      }
    }
    function changeLoadOptions(params,value,updateObject) {
      params.data.value = value
      if(form){
        params.data.data = form.value.submitData()
      }
      ajax(params).then(res => {
        if(res.data){
          for (let field in res.data) {
            setObjectValue(updateObject, field, res.data[field])
          }
        }
      })
    }
    function userRender(slot, scope) {
      return slot.map(item => {
        if(!item) return
        let whereAnd = [],whereOr = []
        if(item.where){
          whereAnd = item.where.AND || []
          whereOr = item.where.OR || []
        }
        if (item && typeof (item.where) == 'object' && (whereAnd.length > 0 || whereOr.length > 0)) {
          // //条件if渲染实现
          return where(whereAnd, whereOr, scope) ? renderComponent(item,scope) : null
        } else {
          if (item && typeof (item) == 'object') {
            return renderComponent(item, scope)
          } else {
            if (item && typeof (item) == 'string' && item.indexOf('#') !== 0) {
              if (checkHtml(item) || checkTemplate(item)) {
                return h({
                  setup() {
                    return {
                      ...modelValue
                    }
                  },
                  template: `${item}`,
                })
              } else {
                return h({
                  setup() {
                    return {
                      ...modelValue
                    }
                  },
                  render() {
                    return item
                  }
                })
              }
            } else {
              return item
            }

          }
        }
      })
    }

    function _createVnode(data, component, attribute, children, directives,scope) {
      delete attribute['data-tag']
      //自定义指令绑定
      let directiveBind = []
      directives = directives || []
      directives.forEach(item => {
        if(scope && scope.row){
          item.argument.updateObject = scope.row
        }
        directiveBind.push([
          resolveDirective(item.name), item.value, item.argument
        ])
      })
      let whereAnd = [],whereOr = []
      if(data.where){
        whereAnd = data.where.AND || []
        whereOr = data.where.OR || []
      }
      if(whereCompile(whereAnd, whereOr,scope,'v-show')){
        directiveBind.push([
          vShow, where(whereAnd, whereOr,scope,'v-show')
        ])
      }
      if (directiveBind.length > 0) {
        if(data.name === 'AMenuItem'){
          return withDirectives(h('div',h(component, attribute, children)), directiveBind)
        }else{

          return withDirectives(h(component, attribute, children), directiveBind)
        }

      } else {
        if (['ExModal'].indexOf(data.name) > -1 && attribute.reference) {
          const reference =  cloneDeep(attribute.reference)
          delete reference.reference
          reference.modal = data
          if (data.init) {
            return [
                h(component, attribute, children),
                renderComponent(reference,scope),
              ]
          } else {
            return renderComponent(reference,scope)
          }
        } else {

          return h(component, attribute, children)
        }
      }
    }


    /**
     * 字符串是否含有html标签的检测
     * @param htmlStr
     */
    function checkHtml(htmlStr) {
      var reg = /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/g;
      let match = htmlStr.match(reg)
      if(match){
        let length = htmlStr.split('<').length - 1
        if(match.length === length){
          return true
        }
      }
      return false
    }

    /**
     * 字符串是否含有{{}}标签的检测
     * @param htmlStr
     */
    function checkTemplate(htmlStr) {
      var reg = /\{\{([\w.]+)\}\}/g;
      return reg.test(htmlStr);
    }
    function where(whereAnd, whereOr, scope,type ='v-if'){
      if (whereAnd.length === 0 && whereOr.length === 0) {
        return true
      }
      const expression = whereCompile(whereAnd, whereOr, scope,type)
      if(!expression){
        return true
      }

      return new Function("return "+expression)()
    }
    /**
     * 合并where
     * @param whereAnd
     * @param whereOr
     * @returns {string}
     */
    function whereCompile(whereAnd, whereOr, scope,type ='v-if') {
      let expressionStr = parseWhere(whereAnd, 'AND', scope,type)
      let expressionOr = parseWhere(whereOr, 'OR', scope,type)
      if (expressionStr && expressionOr) {
        expressionStr += ' || ' + expressionOr
      } else if (expressionOr) {
        expressionStr = expressionOr
      }
      return expressionStr
    }

    /**
     * 解析where
     * @param wheres
     * @param op AND | OR
     * @returns {string}
     */
    function parseWhere(wheres, op, scope,type='v-if') {
      let evals = []
      let expression = ''
      wheres.forEach((where, index) => {

        if (where.where) {
          let expressionStr = whereCompile(where.where.AND, where.where.OR, scope,type)

          if(expressionStr){
            evals.push("(" + expressionStr + ")")
          }
        } else {

          if(type !== where.type){
            return
          }
          let val

          if (scope && scope.row) {
            val = getObjectValue(scope.row,where.field)
          } else {
            val = getObjectValue(modelValue,where.field)
          }

          if (Array.isArray(val)) {
            let conditionValue
            if (scope && scope.row) {
              conditionValue = getObjectValue(scope.row,where.field)
            } else {
              conditionValue = getObjectValue(modelValue,where.field)
            }
            let condition1 = conditionValue.indexOf(where.condition)
            let condition2 = conditionValue.indexOf(`'${where.condition}'`)
            if (where.op == 'notIn') {
              if (isNumber(where.condition)) {
                evals.push( `(${condition1} == -1 && ${condition2} == -1)`)
              } else {
                evals.push(`(${condition2} == -1)`)
              }
            } else {
              if (isNumber(where.condition)) {
                evals.push( `(${condition1} >= 0 || ${condition2} >= 0)`)
              } else {
                evals.push( `(${condition2} >= 0)`)
              }
            }
          } else {
            let operator = where.op
            if (where.op == 'notIn') {
              operator = '!='
            }
            if(val === null || val === undefined){
              val = ''
            }
            evals.push("'" + val + "' " + operator + ' ' + "'" + where.condition + "'")
          }
        }
      })
      if (op == 'AND') {
        op = '&&'
      } else {
        op = '||'
      }
      expression += evals.join(' ' + op + ' ')

      return expression
    }
    function forceUpdate(){
      this.$forceUpdate()
    }

    return {
      grid,
      modelValue,
      where,
      renderComponent,
      setProxyData,
      forceUpdate
    }
  }
})
</script>
